﻿using Stee.CAP8.MessageBus.Sagas.Core.HanlderInvokers;
using Stee.CAP8.MessageBus.Sagas.Core.Sagas;
using Stee.CAP8.MessageBus.Sagas.Messages.Events.Base;
using Stee.CAP8.MessageBus.Sagas.Messages.Messages;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;
using System.Threading;

namespace Stee.CAP8.MessageBus.Sagas.Core.Processes
{
    public class HandlerProvider: IHandlerProvider
    {
        public static ConcurrentDictionary<string, SagaScheduleInfo> sagaInstanceDataItems = new ConcurrentDictionary<string, SagaScheduleInfo>();

        private IServiceProvider _provider;
        private ISagaStorage _sagaStorage;

        public HandlerProvider(IServiceProvider provider, ISagaStorage sagaStorage)
        {
            _provider = provider ?? throw new ArgumentNullException(nameof(provider));
            _sagaStorage = sagaStorage ?? throw new ArgumentNullException(nameof(sagaStorage));

            Thread backGroudScheduler = new Thread(Work)
            {
                IsBackground = true
            };
            backGroudScheduler.Start();

        }

        public void ProcessCommand(Type typeCommonad, object objCommand)
        {
            var methodToInvoke = GetDispatchMethod(typeCommonad);


            var handlerInvoker = ((HandlerInvoker)methodToInvoke.Invoke(this, new[] { objCommand }));
            if (handlerInvoker == null)
            {
                Console.WriteLine($"无法消费的消息{typeCommonad.FullName}");
            }
            else
            {
                if (handlerInvoker.HasSaga)
                {

                    var saga = handlerInvoker.Saga;

                    if (objCommand is ICommandWithIdIdentifier sagaCommand && sagaCommand.SagaId.HasValue)
                    {
                        var sagaData = _sagaStorage.Find(sagaCommand.SagaId.Value).GetAwaiter().GetResult();

                        var sagaType = saga.GetSagaDataType();
                        if (sagaData == null)
                        {

                            sagaData = Activator.CreateInstance(sagaType) as ISagaData;
                            var sagaid = sagaCommand.SagaId.Value;
                            sagaData.Id = sagaid;
                            _sagaStorage.Insert(sagaData);//存储SagaData
                        }
                        else
                        {
                            Console.WriteLine("在Saga存储中取到了SagaData");
                        }
                        handlerInvoker.SetSagaData(sagaData);
                        SaveSagaStep(sagaData, typeCommonad, objCommand);

                    }


                }

                handlerInvoker?.Invoke();
            }
        }




        public void SaveSagaStep(ISagaData sagaData, Type commandType, Object commandInstance)
        {
            var id = sagaData.Id.ToString();

            sagaInstanceDataItems
                .AddOrUpdate(id,
                    id => new SagaScheduleInfo(sagaData, commandInstance, commandType),
                    (id, value) => value);

        }

        private void Work()
        {

            while (true)
            {
                Thread.CurrentThread.Join(1000);
                Console.WriteLine("正在执行后台方法调度 saga,当前class的HashCod=" + this.GetHashCode());
                if (sagaInstanceDataItems == null && sagaInstanceDataItems.Keys.Count == 0)
                {
                    continue;
                }
                ///TODO 这个方法后期优化改成yelid
                foreach (var sagaId in sagaInstanceDataItems.Keys)
                {
                    if (!sagaInstanceDataItems.TryGetValue(sagaId, out var sagaScheduleInfo))
                    {
                        continue;
                    }

                    if (sagaScheduleInfo.IsComplete)
                    {
                        sagaInstanceDataItems.TryRemove(sagaId, out _);
                    }
                    if (sagaScheduleInfo.IsTimeOut())
                    {
                        ProcessCommand(sagaScheduleInfo.CommandType, sagaScheduleInfo.CommandInstance);
                        sagaScheduleInfo.RetryCount++;
                        sagaScheduleInfo.LastRunTime = DateTime.Now;
                    }

                }

            }
        }


        #region private methods
        HandlerInvoker GetHandlerInvoker<TMessage>(TMessage message)
        {
            var handlerService = _provider.GetService<IHandleMessages<TMessage>>();

            if (handlerService == null) return null;
            var handlerInvoke = CreateHandlerInvoker(handlerService, message);
            return handlerInvoke;


        }

        /// <summary>
        /// Creates instance of HandlerInvoker
        /// </summary>
        protected virtual HandlerInvoker CreateHandlerInvoker<TMessage>(IHandleMessages<TMessage> handler, TMessage message)
        {
            return new HandlerInvoker<TMessage>(() => handler.Handle(message), handler);
        }
        MethodInfo GetDispatchMethod(Type messageType)
        {
            const string methodName = nameof(GetHandlerInvoker);

            var genericDispatchMethod = GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance)
                ?? throw new ArgumentException($"Could not find the {methodName} method?!");

            return genericDispatchMethod.MakeGenericMethod(messageType);
        } 
        #endregion

    }
}
